package com.cs249;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;

public class BucketSortPanel extends JPanel {

	private static final long serialVersionUID = 2013110301L;
	private int[] unsortedArray;
	private int[] sortedArray;
	private int bucketSize;
	private int unsortedSize;

	private JButton openFileButton;
	private JButton generateButton;
	private JButton sortButton;
	private JSpinner bucketSizeSpinner;
	private JSpinner unsortedSizeSpinner;
	private JLabel resultTextLabel;
	private JLabel resultLabel;
	private JTextArea unsortedArea;
	private JTextArea sortedArea;

	private JRadioButton integerRadio;
	private JRadioButton binaryRadio;
	private JCheckBox showNumberCheck;
	private JFileChooser fileChooser;

	public int getUnsortedSize() {
		return unsortedSize;
	}

	public void setUnsortedSize(int unsortedSize) {
		this.unsortedSize = unsortedSize;
	}

	public int getBucketSize() {
		return bucketSize;
	}

	public void setBucketSize(int bucketSize) {
		this.bucketSize = bucketSize;
	}

	public void init() {
		unsortedArea = new JTextArea(300, 300);
		sortedArea = new JTextArea(300, 300);
		showNumberCheck = new JCheckBox("Show Number");

		showNumberCheck.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				if (showNumberCheck.isSelected()) {
					displaySortedElements();
					displayUnsortedElements();
				} else {
					sortedArea.setText("");
					unsortedArea.setText("");
				}

			}
		});
		showNumberCheck.setSelected(false);

		JLabel sizeLabel = new JLabel("Bucket Size");

		SpinnerModel bucketModel = new SpinnerNumberModel(1000000, 2, 100000000,
				1);
		bucketSizeSpinner = new JSpinner(bucketModel);

		SpinnerModel unsortedModel = new SpinnerNumberModel(1000000, 2,
				100000000, 1);
		unsortedSizeSpinner = new JSpinner(unsortedModel);

		resultTextLabel = new JLabel("Total running time (ms): ");
		resultLabel = new JLabel("0");

		sortButton = new JButton("Sort");
		sortButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				// TODO Auto-generated method stub

				SwingUtilities.invokeLater(new Runnable() {

					@Override
					public void run() {
						// TODO Auto-generated method stubfinal
						integerRadio.setEnabled(true);
						binaryRadio.setEnabled(true);

						long totalTime = sort(unsortedArray,
								unsortedArray.length);
						resultLabel.setText("" + totalTime);
						if (showNumberCheck.isSelected()) {
							displaySortedElements();
						}

					}
				});

			}
		});

		fileChooser = new JFileChooser();
		openFileButton = new JButton("Open File");
		openFileButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				int returnVal = fileChooser.showOpenDialog(BucketSortPanel.this);

				if (returnVal == JFileChooser.APPROVE_OPTION) {
					File file = fileChooser.getSelectedFile();

					unsortedArray = getArrayFrom(file);
				}
			}
		});

		generateButton = new JButton("Generate");
		generateButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				integerRadio.setEnabled(false);
				binaryRadio.setEnabled(false);

				sortedArea.setText("");
				resultLabel.setText("0");

				int size = (Integer) unsortedSizeSpinner.getValue();
				setBucketSize(size);
				unsortedArray = generateUnsortedArray(size);

				int returnVal = fileChooser.showSaveDialog(BucketSortPanel.this);
				if (returnVal != JFileChooser.APPROVE_OPTION) {
					return;
				}
				writeToFile(fileChooser.getSelectedFile());

				if (showNumberCheck.isSelected()) {
					displayUnsortedElements();
				}
			}
		});

		integerRadio = new JRadioButton("Integer");
		integerRadio.setSelected(true);
		integerRadio.setEnabled(false);
		integerRadio.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				unsortedArea.setText("");
				sortedArea.setText("");

				for (int i = 0; i < unsortedArray.length; i++) {
					unsortedArea.append(unsortedArray[i] + "");
					sortedArea.append(sortedArray[i] + "");
					if (i != (unsortedArray.length - 1)) {
						unsortedArea.append(", ");
						sortedArea.append(", ");
					}

					if ((i != 0) && (i % 10 == 0)) {
						unsortedArea.append("\n");
						sortedArea.append("\n");
					}
				}
			}
		});

		binaryRadio = new JRadioButton("Binary");
		binaryRadio.setEnabled(false);
		binaryRadio.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				unsortedArea.setText("");
				sortedArea.setText("");
				for (int i = 0; i < unsortedArray.length; i++) {
					unsortedArea.append(Integer
							.toBinaryString(unsortedArray[i]) + "");
					sortedArea.append(Integer.toBinaryString(sortedArray[i])
							+ "");
					if (i != (unsortedArray.length - 1)) {
						unsortedArea.append(", ");
						sortedArea.append(", ");
					}
					if ((i != 0) && (i % 6 == 0)) {
						unsortedArea.append("\n");
						sortedArea.append("\n");
					}
				}
			}
		});

		ButtonGroup group = new ButtonGroup();
		group.add(integerRadio);
		group.add(binaryRadio);
		JPanel radioPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		// radioPanel.setBorder(BorderFactory.createTitledBorder("Show"));
		radioPanel.setBorder(BorderFactory.createEtchedBorder());
		radioPanel.add(showNumberCheck);
		radioPanel.add(integerRadio);
		radioPanel.add(binaryRadio);

		JPanel northPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		northPanel.add(sizeLabel);
		northPanel.add(bucketSizeSpinner);
		northPanel.add(new JLabel("Unsort Size"));
		northPanel.add(unsortedSizeSpinner);
		northPanel.add(openFileButton);
		northPanel.add(generateButton);
		northPanel.add(sortButton);

		JScrollPane unSortedScrollPane = new JScrollPane(unsortedArea);
		JScrollPane sortedScrollPane = new JScrollPane(sortedArea);

		JPanel textAreaPanel = new JPanel(new GridLayout(1, 2, 5, 5));
		textAreaPanel.add(unSortedScrollPane);
		textAreaPanel.add(sortedScrollPane);

		JPanel centerPanel = new JPanel(new BorderLayout());
		centerPanel.add(radioPanel, BorderLayout.NORTH);
		centerPanel.add(textAreaPanel, BorderLayout.CENTER);

		JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		southPanel.add(resultTextLabel);
		southPanel.add(resultLabel);

		setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
		setLayout(new BorderLayout(5, 5));
		add(northPanel, BorderLayout.NORTH);
		add(centerPanel, BorderLayout.CENTER);
		add(southPanel, BorderLayout.SOUTH);

		setPreferredSize(new Dimension(1000, 500));
	}

	private long sort(int[] unsortedArray2, int N) {
		long start = System.currentTimeMillis();

		sortedArray = new int[unsortedArray2.length];
		for (int i = 0; i < unsortedArray2.length; i++) {
			sortedArray[i] = unsortedArray2[i];
		}

		// Initialize buckets.
		int[] buckets = new int[N];
		for (int i = 0; i < N; i++) {
			buckets[i] = 0;
		}

		// Scatter.
		for (int i = 0; i < N; i++) {
			++buckets[sortedArray[i]];
		}

		// Gather.
		int counter = 0;
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < buckets[i]; j++) {
				sortedArray[counter] = i;
				counter++;
			}
		}

		long end = System.currentTimeMillis();
		long total = end - start;

		System.out.println();
		System.out.println("Total running time (msec): " + total);

		return total;

	}

	/**
	 * Create the GUI and show it. For thread safety, this method should be
	 * invoked from the event-dispatching thread.
	 */
	private static void createAndShowGUI() {
		// Create and set up the window.
		JFrame frame = new JFrame("Bucket Sort");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		BucketSortPanel bs = new BucketSortPanel();
		bs.init();
		frame.getContentPane().add(bs, BorderLayout.CENTER);

		// Display the window.
		frame.pack();
		frame.setVisible(true);

	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createAndShowGUI();
			}
		});

	}

	private int[] generateUnsortedArray(int size) {
		int[] a = new int[size];
		for (int i = 0; i < size; i++) {
			Random random = new Random();
			a[i] = random.nextInt(size);
		}
		return a;
	}

	private void displayUnsortedElements() {
		if (unsortedArray == null) {
			return;
		}

		StringBuilder stringBuilder = new StringBuilder();
		for (int i = 0; i < unsortedArray.length; i++) {
			if (integerRadio.isSelected()) {
				stringBuilder.append(unsortedArray[i] + "");
				if (i != (unsortedArray.length - 1)) {
					stringBuilder.append(", ");
				}
				if ((i != 0) && (i % 10 == 0)) {
					stringBuilder.append("\n");
				}
			} else {
				stringBuilder.append(Integer.toBinaryString(unsortedArray[i]));
				if (i != (unsortedArray.length - 1)) {
					stringBuilder.append(", ");
				}
				if ((i != 0) && (i % 6 == 0)) {
					stringBuilder.append("\n");
				}
			}

		}
		unsortedArea.setText("");
		unsortedArea.append(stringBuilder.toString());

	}

	private void writeToFile(File file) {
		try {
			// File file = new File("/Users/hiepst/dataSet" +
			// unsortedArray.length
			// + ".txt");
			// file.createNewFile();
			BufferedWriter bw = new BufferedWriter(new FileWriter(
					file.getAbsoluteFile()));
			String newline = System.getProperty("line.separator");

			for (int i = 0; i < unsortedArray.length; i++) {
				bw.write(unsortedArray[i] + ",");
				if (i != 0 && i % 10 == 0) {
					bw.write(newline);
				}
			}
			bw.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void displaySortedElements() {
		if (sortedArray == null) {
			return;
		}

		StringBuilder stringBuilder = new StringBuilder();
		for (int i = 0; i < sortedArray.length; i++) {
			if (integerRadio.isSelected()) {
				stringBuilder.append(sortedArray[i] + "");
				if (i != (sortedArray.length - 1)) {
					stringBuilder.append(", ");
				}
				if ((i != 0) && (i % 10 == 0)) {
					stringBuilder.append("\n");
				}
			} else {
				stringBuilder.append(Integer.toBinaryString(sortedArray[i]));
				if (i != (sortedArray.length - 1)) {
					stringBuilder.append(", ");
				}
				if ((i != 0) && (i % 6 == 0)) {
					stringBuilder.append("\n");
				}
			}

		}
		sortedArea.setText("");
		sortedArea.append(stringBuilder.toString());
	}

	private int[] getArrayFrom(File file) {
		BufferedReader reader;
		String fileName = file.getName();
		System.out.println(fileName);

		int endIndex = fileName.indexOf(".");
		String total = fileName.substring(7, endIndex);

		int size = Integer.parseInt(total);
		System.out.println(size);
		int[] array = new int[size];
		int index = 0;
		try {

			reader = new BufferedReader(new FileReader(file));
			String line = null;

			while ((line = reader.readLine()) != null) {
				String[] split = line.split(",");
				for (int i = 0; i < split.length; i++) {
					int value = Integer.parseInt(split[i]);
					array[index] = value;
					index++;
				}
			}
		} catch (IOException x) {
			System.err.format("IOException: %s%n", x);
		}

		return array;
	}

}
